/*
 * Specimen.java
 *
 * Created on January 9, 2008, 5:47 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package engine;

import java.util.*;


/**
 * Osobnik
 * @author Shade
 */
public class Specimen implements Cloneable, Comparable, SpecInterface{
    private byte[][] edgesList;
    private int adaptationRatio = 0;
    
    /**konstruktor klasy
     * przyjmuje wartosc wielkosci tablicy - czyli ilosc wierzcholkow
     * definicja tablicy edgesList - do zrobienia
     * 
     */
    public Specimen() {
        edgesList = Enviroment.specimenPrototype.clone();       //shallow clone
        for(int i=0; i < edgesList.length; i++)                 // deepen the shallow clone
            edgesList[i] = Enviroment.specimenPrototype[i].clone();
        
        for (int i=0 ; i<edgesList.length ; i++){
            for (int j=i+1 ; j <edgesList.length  ; j++){
                if (edgesList[i][j] != 0){
                   edgesList[i][j] = edgesList[j][i] =  (byte)(Enviroment.random.nextInt(Enviroment.maxColors-1)+1);
                }
            }
        }
        refreshAdaptationRatio();
    }
    /** konstruktor tworzacy osobnika na podstawie zadanej tablicy
     * @param prototype propotyp osobnika
     * @param adapt stopień adaptacji
     */
    public Specimen(byte[][] prototype, int adapt) {
        edgesList = prototype.clone();                          //shallow clone
        for(int i=0; i < edgesList.length; i++)                 // deepen the shallow clone
            edgesList[i] = prototype[i].clone();
        
        adaptationRatio = adapt;
    }
    /** calculate punishment*/
	private int getPunishment(){
            int currPunish = 0;                                                 //aktualnie naliczona kara         
            for (int i=0; i<edgesList.length ; i++){
                int[] colors = new int[this.colorCount()+1];                                     //tablica kolorow
           
                int repeatInRow = 0;                                            
                for (int j=0; j<edgesList.length ; j++){
                    if (edgesList[i][j] > 0){
                        colors[edgesList[i][j]]++;
                    }
                }
                for (int k=1 ; k<colors.length ; k++){
                    if(colors[k] != 0){
                        colors[k]--;
                        repeatInRow += colors[k];
                    }
                }
                 currPunish += (repeatInRow * repeatInRow);
            }
            return currPunish;
    }
    /** metoda pobiera kolor krawedzi(gen)
     * @param searchGene wyszukiwane geny
     * @return zwraca wartosc -1 jezeli podany gen nie istenije
     */
    public byte getGene(byte searchGene) {
        int currGene = 0;                                                        //aktualny czytany Gen
        for (int i=0 ; i<edgesList.length ; i++){
            for (int j=i+1 ; j<edgesList.length ; j++){
                if (edgesList[i][j] != 0){
                    if (currGene == searchGene){
                        return edgesList[i][j];
                    }
                    currGene++;
                }
            }
        }
        return 0;
    }
    /** metoda ustawia kolor krawedzi(gen)
     * @param searchGene szukane geny
     * @param color wartość koloru
     * @return zwraca -1 jezeli podany gen nie istnieje
     */
    public byte setGene(byte searchGene, byte color) {
         int currGene = 0;                                                        //aktualny czytany Gen
        for (int i=0 ; i<edgesList.length ; i++){
            for (int j=i+1 ; j<edgesList.length ; j++){
                if (edgesList[i][j] != 0){
                    if (currGene == searchGene){
                        edgesList[i][j] = edgesList[j][i] = color;               
                        return 1;
                    }
                    currGene++;
                }  
            }
        }
        return 0;
    }
    /**metoda optymalizuje kolory uzyte do pokolorowania grafu
     */
    public void optimizeColors() {
        byte lastColor = 0;
        byte quantum = 0; 
        for (int i=0 ; i<edgesList.length ; i++){
            for (int j=i+1 ; j<edgesList.length ; j++){
                if (edgesList[i][j] > quantum){
                    quantum = edgesList[i][j];
                }
            }
        }
        /***********************************************************/
        boolean isEnd = false;
        for (byte q=1 ; q<quantum + 1 ; q++){
            isEnd = false;
            for (int i=0 ; i<edgesList.length ; i++){
                for (int j=i+1 ; j<edgesList.length ; j++){
                    if (edgesList[i][j] == q){
                        isEnd = true;
                        lastColor++;
                        changeColors(q,lastColor);
                        break;
                    }
                }
                if(isEnd == true)
                    break;
            }
        }
    }
    private void changeColors(byte toChange, byte newColor){
        for (int i=0 ; i<edgesList.length ; i++){
                for (int j=i+1 ; j<edgesList.length ; j++){
                    if (edgesList[i][j] == toChange)
                        edgesList[i][j] = edgesList[j][i] = newColor;
                }
            }
    }
    /**metoda zlicza ilosc uzytych kolorow do pokolorowania grafu tego osobnika
     * @return liczba kolorów
     */
    public byte colorCount() {
       optimizeColors();
        byte quantum = 0;                                                       //ilosc zliczonych aktualnie kolorow
        for (int i=0 ; i<edgesList.length ; i++){
            for (int j=i+1 ; j<edgesList.length ; j++){
                if (edgesList[i][j] > quantum){
                    quantum = edgesList[i][j];
                }
            }
        }
        return quantum;
    }
    /** Zwraca macierz genów osobnika
     * @return macierz genów osobnika
     */
    public byte[][] getDNA() {
        return edgesList;
    }
    /** metoda zwraca wspolczynnik przystosowania
     * @return współczynnik przystosowania
     */
    public int getAdaptation(){
        return this.adaptationRatio;
    }
    /** metoda sprawdza poprawnosc osobnika - grafu
     * DO PRZEMYSLENIA
     * @return true/false poprawny/niepoprawny
     */
     public boolean isValid() {
        return true;
    }
     /** zwraca swoja kopie
      * @return kopa osobnika
      */
    public Specimen clone(){
        Specimen newOne = new Specimen(this.edgesList, this.getAdaptation());
        return newOne;
    }
    /** porownuje osobnika z innym

     *
     *adaptationRatio - wspolczynnik przystosowania
     * @param other
     * @return zwraca -1 jezeli TEN osobnik ma mniejszy wspolczynnik przystosowania
     *zwraca 1 jezeli TEN osobnik ma wiekszy wspolczynnik przystosowania
     *zwraca 0 jezelu osobniki maja taki sam wspolczynnik przystosowania
     */
    public int compareTo(Object other){
        if (((Specimen) other).adaptationRatio > adaptationRatio)
            return -1;
        if (((Specimen) other).adaptationRatio < adaptationRatio)
            return 1;        
        return 0;
    }
    /** metoda oblicza wspolczynnik przystosowania osobnika i przypisuje do adaptationRatio*/
    public void refreshAdaptationRatio(){
       this.adaptationRatio = Enviroment.m1 * this.colorCount() + Enviroment.m2 * this.getPunishment();
    }
    /** metoda debugujaca do wypisywania macierzy
     */
    public void debugPrintMatrix(){
        System.out.println("Moj genotyp: ");
        for (int i = 0; i<edgesList.length ; i++){
            for (int j = 0; j<edgesList.length ; j++){
                System.out.print(edgesList[i][j] + " ");
            }
            System.out.println();
        }
    }
    private void debugAdaptation(){
        System.out.println("adaptationRatio: " + this.getAdaptation());
    }
    private void debugTestGeneMethod(byte n){
         System.out.println(this.getGene(n));
         this.setGene(n,(byte)9);
         System.out.println(this.getGene(n));
    }
}
